
local inspect = require "ddcl.inspect"

local function desc_table(client, tname)
    local sql = string.format("show full columns from `%s`", tname)
    local res = client.query(sql)
    if res.err then
        return res.err
    end
    return NO_ERR, res
end

local function create_table(client, name, fields, prikey, comment)
    local sql = {}
    table.insert(sql, string.format("CREATE TABLE IF NOT EXISTS `%s` (", name))
    for k, v in ipairs(fields) do
        local fs = string.format("`%s` %s %s NULL %s COMMENT \"%s\",",
                                    v.name, v.type, v.null or "", v.extra or "", v.comment or "")
        table.insert(sql, fs)
    end
    if prikey then
        table.insert(sql, string.format("PRIMARY KEY (%s)", prikey))
    end
    table.insert(sql, string.format(") COMMENT \"%s\";", comment or ""))
    local sqltr = table.concat(sql, "\n")
    print("create table:\n", sqltr)
    local res = client.query(sqltr)
    if res.err then
        return res.err
    end
    return NO_ERR, res
end

local function drop_table(client, name)
    local sql = string.format("DROP TABLE `%s`;", name)
    print("drop table:\n", name)
    local res = client.query(sql)
    if res.err then
        return res.err
    end
    return NO_ERR, res
end

local function insert_field(client, table_name, field_name, field)
    local sql = string.format(
        "ALTER TABLE `%s` ADD COLUMN `%s` %s %s NULL %s COMMENT \"%s\";",
        table_name, field_name, field.type, field.null or "", field.extra or "", field.comment or "")
    print("insert field:\n", sql)
    local res = client.query(sql)
    if res.err then
        return res.err
    end
    return NO_ERR, res
end

local function modify_field(client, table_name, field_name, field)
    local sql = string.format(
        "alter table `%s` change `%s` `%s` %s %s NULL %s COMMENT \"%s\";",
        table_name, field_name, field_name, field.type, field.null or "", field.extra or "", field.comment or "")
    print("modify field:\n", sql)
    local res = client.query(sql)
    if res.err then
        return res.err
    end
    return NO_ERR, res
end

local function check_fields(client, table_name, fields)
    local err, descs = desc_table(client, table_name)
    local cur_fields = {}
    for i, v in pairs(descs) do
        cur_fields[v.Field] = {
            type = v.Type,
            null = v.Null == "NO" and "NOT" or "",
            extra = v.Extra,
            comment = v.Comment,
        }
    end

    local creates = {}
    local executes = {}

    for i, v in ipairs(fields) do
        if cur_fields[v.name] then
            executes[v.name] = v
        else
            assert(v.name, v.comment)
            creates[v.name] = v
        end
    end

    for k, v in pairs(creates) do
        local err, res = insert_field(client, table_name, k, v)
        assert(not err, err)
    end

    local ckmap = { "type", "null", "extra", "comment", }
    for k, v in pairs(executes) do
        local sv = cur_fields[k]
        for i, f in ipairs(ckmap) do
            if v[f] and sv[f] and string.upper(v[f]) ~=  string.upper(sv[f]) then
                print("field diff:", f, v[f], "~=", sv[f])
                local err, res = modify_field(client, table_name, k, v)
                assert(not err, err)
                break;
            end
        end
    end
end





return function(client, registers)
    local query_tables = client.query("show tables;")
    local cur_tables = {}
    for i, v in ipairs(query_tables) do
        local n = v[next(v)]
        cur_tables[n] = n
    end

    local creates = {}
    local deletes = {}
    local executes = {}

    for k, v in pairs(cur_tables) do
        if not registers[k] then
            deletes[k] = k
        else
            executes[k] = registers[k]
        end
    end

    for k, v in pairs(registers) do
        if not cur_tables[k] then
            creates[k] = v
        end
    end

    for k, v in pairs(creates) do
        local err, res = create_table(client, k, v.fields, v.prikey, v.comment)
        assert(not err, err)
    end

    --[[ not delete table
    for k, v in pairs(deletes) do
        local err, res = drop_table(client, k)
        assert(not err, err)
    end
    --]]

    for k, v in pairs(executes) do
        check_fields(client, k, v.fields)
    end
end
